{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Programming Microblaze Subsystems from Jupyter\n",
    "\n",
    "In the Base I/O overlay that accompany the PYNQ release Microblazes are used to control peripherals attached to the various connectors. These can either be programmed with existing programs compiled externally or from within Jupyter. This notebook explains how the Microblazes can be integrated into Jupyter and Python.\n",
    "\n",
    "The Microblaze is programmed in C. The PYNQ framework provides a mechanism to write the C code inside Jupyter, compile it, load it on to the Microblaze and then execute and interact with it.\n",
    "\n",
    "The first step is to load an overlay."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pynq.overlays.base import BaseOverlay\n",
    "\n",
    "base = BaseOverlay('base.bit')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can write some C code. The `%%microblaze` magic provides an environment where we can write the code and it takes a single argument - the Microblaze we wish to target this code at. This first example simply adds two numbers together and returns the result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%microblaze base.ARDUINO\n",
    "\n",
    "int add(int a, int b) {\n",
    "    return a + b;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The functions we defined in the magic are now available for us to interact with in Python as any other function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add(4,6)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Data Motion\n",
    "\n",
    "The main purpose of the Python bindings it to transfer data between the host and slave processors. For simple cases, any primitive C type can be used as function parameters and return values and Python values will be automatically converted as necessary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%microblaze base.ARDUINO\n",
    "\n",
    "float arg_passing(float a, char b, unsigned int c) {\n",
    "    return a + b + c;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "arg_passing(1, 2, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Arrays can be passed in two different ways. If a type other than `void` is provided then the data will be copied to the MicroBlaze and if non-`const` the data will be copied back as well. And iterable and modifiable object can be used as the argument in this case."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%microblaze base.ARDUINO\n",
    "\n",
    "int culm_sum(int* val, int len) {\n",
    "    int sum = 0;\n",
    "    for (int i = 0; i < len; ++i) {\n",
    "        sum += val[i];\n",
    "        val[i] = sum;\n",
    "    }\n",
    "    return sum;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "numbers = [i for i in range(10)]\n",
    "culm_sum(numbers, len(numbers))\n",
    "print(numbers)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally we can pass a `void` pointer which will allow the Microblaze to directly access the memory of the host processing system for transferring large quantities of data. In Python these blocks of memory should be allocated using the `Xlnk.cma_array` function and it is the responsibility of the programmer to make sure that the Python and C code agree on the types used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%microblaze base.ARDUINO\n",
    "\n",
    "long long big_sum(void* data, int len) {\n",
    "    int* int_data = (int*)data;\n",
    "    long long sum = 0;\n",
    "    for (int i = 0; i < len; ++i) {\n",
    "        sum += int_data[i];\n",
    "    }\n",
    "    return sum;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pynq import Xlnk\n",
    "allocator = Xlnk()\n",
    "\n",
    "buffer = allocator.cma_array(shape=(1024 * 1024), dtype='i4')\n",
    "buffer[:] = range(1024*1024)\n",
    "\n",
    "big_sum(buffer, len(buffer))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Debug printing\n",
    "\n",
    "One unique feature of the PYNQ Microblaze environment is the ability to print debug information directly on to the Jupyter or Python console using the new `pyprintf` function. This functions acts like `printf` and `format` in Python and allows for a format string and variables to be passed back to Python for printing. In this release on the `%d` format specifier is supported but this will increase over time.\n",
    "\n",
    "To use `pyprintf` first the appropriate header needs to be included"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%microblaze base.ARDUINO\n",
    "#include <pyprintf.h>\n",
    "\n",
    "int debug_sum(int a, int b) {\n",
    "    int sum = a + b;\n",
    "    pyprintf(\"Adding %d and %d to get %d\\n\", a, b, sum);\n",
    "    return sum;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "debug_sum(1,2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Long running processes\n",
    "\n",
    "So far all of the examples presented have been synchronous with the Python code with the Python code blocking until a result is available. Some applications call instead for a long-running process which is periodically queried by other functions. If a C function return `void` then the Python process will resume immediately leaving the function running on its own.\n",
    "\n",
    "Other functions can be run while the long-running process is active but as there is no pre-emptive multithreading the persistent process will have to `yield` at non-timing critical points to allow other queued functions to run.\n",
    "\n",
    "In this example we launch a simple counter process and then pull the value using a second function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%microblaze base.ARDUINO\n",
    "#include <yield.h>\n",
    "\n",
    "static int counter = 0;\n",
    "\n",
    "void start_counter() {\n",
    "    while (1) {\n",
    "        ++counter;\n",
    "        yield();\n",
    "    }\n",
    "}\n",
    "\n",
    "int counter_value() {\n",
    "    return counter;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now start the counter going."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "start_counter()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And interrogate its current value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "counter_value()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are some limitations with using `pyprintf` inside a persistent function in that the output will not be displayed until a subsequent function is called. If the buffer fills in the meantime this can cause the process to deadlock.\n",
    "\n",
    "Only one persistent process can be called at once - if another is started it will block the first until it returns. If too many processes are stacked in this way a stack overflow may occur leading to undefined results."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating class-like objects\n",
    "\n",
    "In the C code `typedef`s can be used to create psuedo classes in Python. If you have a `typedef` called `my_class` then any functions that being `my_class_` are assumed to be associated with it. If one of those functions takes `my_class` as the first argument it is taken to be equivalent to `self`. Note that the `typedef` can only ultimately refer a primitive type. The following example does some basic modular arithmetic base 53 using this idiom."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%microblaze base.ARDUINO\n",
    "\n",
    "typedef unsigned int mod_int;\n",
    "\n",
    "mod_int mod_int_create(int val) { return val % 53; }\n",
    "mod_int mod_int_add(mod_int lhs, int rhs) { return (lhs + rhs) % 53; }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now create instances using our `create` function and call the `add` method on the returned object. The underlying value of the typedef instance can be retrieved from the `.val` attribute."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = mod_int_create(63)\n",
    "b = a.add(4)\n",
    "print(b)\n",
    "print(b.val)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Coding Guidelines for Microblaze Interfacing Code\n",
    "\n",
    "There are some limitations to be aware of in the Jupyter integration with the Microblaze subsystem in particular the following things are unsupported and will result in the function not being available.\n",
    "\n",
    " * `struct`s or `union`s of any kind\n",
    " * Pointers to pointers\n",
    " * returning pointers\n",
    " \n",
    "All non `void*` parameters are passed on the stack so beware of passing large arrays in this fashion or a stack overflow will result."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
